﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Teal.Compiler {

    /// <summary>
    /// 表示一个编译器。用于存储相关的配置数据以及提供公共的编译辅助函数。
    /// </summary>
    public static partial class Compiler {

        #region 错误信息输出

        /// <summary>
        /// 获取或设置当前用于报告信息的对象。
        /// </summary>
        public static IReporter reportor = new ConsoleReporter();

        /// <summary>
        /// 获取当前产生的错误数。
        /// </summary>
        public static int errorCount {
            get { return currentPackage.errorCount; }
        }

        /// <summary>
        /// 获取当前产生的警告数。
        /// </summary>
        public static int warningCount {
            get { return currentPackage.warningCount; }
        }

        /// <summary>
        /// 报告一个错误。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">错误信息。</param>
        /// <param name="startLocation">错误在源码内的起始位置。</param>
        /// <param name="endLocation">错误在源码内的结束位置。</param>
        public static void error(ErrorCode code, string message, Location startLocation, Location endLocation) {
            currentPackage.errorCount++;
            if (errorCount == Compiler.options.maxErrorCount) {
                throw new FatalException("错误数超过 “{0}” 个，已停止编译");
            }
            reportor.error(code, message, startLocation, endLocation);
        }

        /// <summary>
        /// 报告一个错误。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">错误信息。</param>
        /// <param name="Token">引发本错误的原始标记。</param>
        public static void error(ErrorCode code, string message, Token token) {
            error(code, message, token.startLocation, token.endLocation);
        }

        /// <summary>
        /// 报告一个错误。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">错误信息。</param>
        /// <param name="srcNode">引发本错误的原始语法树节点。</param>
        public static void error(ErrorCode code, string message, Node srcNode) {
            error(code, message, srcNode.startLocation, srcNode.endLocation);
        }

        /// <summary>
        /// 报告一个错误。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">错误信息。</param>
        /// <param name="srcNode">引发本错误的原始语法树节点。</param>
        public static void error(ErrorCode code, string message) {
            error(code, message, new Location(), new Location());
        }

        /// <summary>
        /// 报告一个警告。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="startLocation">警告在源码内的起始位置。</param>
        /// <param name="endLocation">警告在源码内的结束位置。</param>
        public static void warning(ErrorCode code, string message, Location startLocation, Location endLocation) {
            currentPackage.warningCount++;
            reportor.warning(code, message, startLocation, endLocation);
        }

        /// <summary>
        /// 报告一个警告。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="srcNode">引发本错误的原始语法树节点。</param>
        public static void warning(ErrorCode code, string message, Node srcNode) {
            warning(code, message, srcNode.startLocation, srcNode.endLocation);
        }

        /// <summary>
        /// 报告一个警告。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="srcNode">引发本错误的原始语法树节点。</param>
        public static void warning(ErrorCode code, string message) {
            warning(code, message, new Location(), new Location());
        }

        /// <summary>
        /// 报告一个警告。
        /// </summary>
        /// <param name="code">警告序号。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="startLocation">警告在源码内的起始位置。</param>
        /// <param name="endLocation">警告在源码内的结束位置。</param>
        public static void echo(string message, Location startLocation, Location endLocation) {
            reportor.echo(message, startLocation, endLocation);
        }

        /// <summary>
        /// 报告一个警告。
        /// </summary>
        /// <param name="code">警告序号。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="startLocation">警告在源码内的起始位置。</param>
        /// <param name="endLocation">警告在源码内的结束位置。</param>
        public static void echo(string message) {
            echo(message, new Location(), new Location());
        }

        internal static void notImplement(Node node, string name = null) {
            echo(name == null ? ("此语法正确，但是 Teal 的当前版本暂不支持解析：" + node.ToString()) : ("此语法正确，但是 Teal 的当前版本暂不支持 " + name + "：" + node.ToString()), node.startLocation, node.endLocation);
        }

        public static void disableReportor() {

        }

        public static void resumeReportor(bool flush) {

        }

        #endregion

    }

    /// <summary>
    /// 表示一个用于报告错误和警告信息的报告器。
    /// </summary>
    public interface IReporter {

        /// <summary>
        /// 输出一个错误信息。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">要输出的信息内容。</param>
        /// <param name="startLocation">引发此信息的源码起始位置。</param>
        /// <param name="endLocation">引发此信息的源码结束位置。</param>
        void error(ErrorCode code, string message, Location startLocation, Location endLocation);

        /// <summary>
        /// 输出一个警告信息。
        /// </summary>
        /// <param name="code">信息代码。</param>
        /// <param name="message">要输出的信息内容。</param>
        /// <param name="startLocation">引发此信息的源码起始位置。</param>
        /// <param name="endLocation">引发此信息的源码结束位置。</param>
        void warning(ErrorCode code, string message, Location startLocation, Location endLocation);

        /// <summary>
        /// 输出一个信息。
        /// </summary>
        /// <param name="message">要输出的信息内容。</param>
        /// <param name="startLocation">引发此信息的源码起始位置。</param>
        /// <param name="endLocation">引发此信息的源码结束位置。</param>
        void echo(string message, Location startLocation, Location endLocation);

    }

    /// <summary>
    /// 表示一个使用控制台进行报告的报告器。
    /// </summary>
    public class ConsoleReporter : IReporter {

        /// <summary>
        /// 输出一个错误信息。
        /// </summary>
        /// <param name="errorNumber">错误序号。</param>
        /// <param name="message">错误信息。</param>
        /// <param name="suggestions">所有可用的修改建议。</param>
        /// <param name="startLocation">错误在源码内的起始位置。</param>
        /// <param name="endLocation">错误在源码内的结束位置。</param>
        public void error(ErrorCode errorNumber, string message, Location startLocation, Location endLocation) {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("{0}({1},{2}-{3},{4}): TE{5:0000}: {6}", startLocation.sourcePath, startLocation.row, startLocation.column, endLocation.row, endLocation.column, (int)errorNumber, message);
            Console.ResetColor();
        }

        /// <summary>
        /// 输出一个警告信息。
        /// </summary>
        /// <param name="warningNumber">警告序号。</param>
        /// <param name="message">警告信息。</param>
        /// <param name="suggestions">所有可用的修改建议。</param>
        /// <param name="startLocation">警告在源码内的起始位置。</param>
        /// <param name="endLocation">警告在源码内的结束位置。</param>
        public void warning(ErrorCode warningNumber, string message, Location startLocation, Location endLocation) {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("{0}({1},{2}-{3},{4}): TW{5:0000}: {6}", startLocation.sourcePath, startLocation.row, startLocation.column, endLocation.row, endLocation.column, (int)warningNumber, message);
            Console.ResetColor();
        }

        /// <summary>
        /// 输出一个信息。
        /// </summary>
        /// <param name="message">要输出的信息。</param>
        /// <param name="startLocation">产生此信息的起始位置。</param>
        /// <param name="endLocation">产生此信息的结束位置。</param>
        public void echo(string message, Location startLocation, Location endLocation) {
            Console.WriteLine("{0}({1},{2}-{3},{4}): 信息 {5}", startLocation.sourcePath, startLocation.row, startLocation.column, endLocation.row, endLocation.column, message);
        }

        /// <summary>
        /// 清空输出的信息。
        /// </summary>
        public void clear() {
            Console.Clear();
        }

    }

    /// <summary>
    /// 表示一个临时错误报告器。
    /// </summary>
    public sealed class SessionReportor : IReporter {

        /// <summary>
        /// 获取或设置当前临时报告器之前的报告器。
        /// </summary>
        public IReporter prev;

        private struct ErrorMessage {

            public bool isError;
            public ErrorCode code;
            public string message;
            public Location startLocation;
            public Location endLocation;

        }

        List<ErrorMessage> _errors = ListObjectPool<ErrorMessage>.alloc();

        public void error(ErrorCode code, string message, Location startLocation, Location endLocation) {
            _errors.Add(new ErrorMessage() {
                isError = true,
                code = code,
                message = message,
                startLocation = startLocation,
                endLocation = endLocation
            });
        }

        public void warning(ErrorCode code, string message, Location startLocation, Location endLocation) {
            _errors.Add(new ErrorMessage() {
                isError = true,
                code = code,
                message = message,
                startLocation = startLocation,
                endLocation = endLocation
            });
        }

        public void echo(string message, Location startLocation, Location endLocation) {
            _errors.Add(new ErrorMessage() {
                code = ErrorCode.none,
                message = message,
                startLocation = startLocation,
                endLocation = endLocation
            });
        }

        public void clear() {
            _errors.Clear();
        }

        /// <summary>
        /// 报告所有被当前临时报告期缓存的错误信息。
        /// </summary>
        public int flush() {
            foreach (var error in _errors) {
                if (error.isError) {
                    prev.error(error.code, error.message, error.startLocation, error.endLocation);
                } else {
                    prev.warning(error.code, error.message, error.startLocation, error.endLocation);
                }
            }
            int count = _errors.Count;
            ListObjectPool<ErrorMessage>.free(_errors);
            return count;
        }

    }

}